#pragma once

#include "Pattern.h"
#include "MatchStructs.h"
#include "MatchTemplate.h"

namespace SpikingNeuronLib
{
	public delegate void MatchedEventHandler(MatchResult);
	public delegate void ScanUpdateEventHandler(int, int);

	public ref class MatchScanner
	{
	private:
        List<int>^ _fileIndices;						// a file index uniquely indexes a firings file using the number of engine runtime seconds at the time the file was written
		Dictionary<int, String^>^ _filesDictionary;		// maps file indices to full file paths
        Dictionary<int, int>^ _sequentialLookup;		// maps a sequential sequence to the sequence of file indices (the latter are sequential only if a file was written for every second)
		array<int, 2>^ _spikeRaster;					// a spike raster view of the data being scanned (view limited to TimeAxisLength milliseconds of the data)
		Dictionary<int, CacheData^>^ _fileCache;		// a cache of retrieved file data indexed by file index

		Dictionary<int, List<FiringEvent>^>^ _dataDictionary;	// maps time indices to firing data (for in-memory scans)

        int _readFileIndex;								// the file index of the firings file for the read segment
        int _writeFileIndex;							// the file index of the firings file for the write segment
		int _numberOfNeurons;							// the number of neurons in the firing data (the data being scanned and matched)
		int _jitter;									// the match error (a firing is matched if the firing time (t) in the template is the same as in the firing data +/- jitter i.e. t +/- jitter)
		int _offset;									// the offset into the spike raster view for alternately reading and writing
		int _nMatches;									// the number of matches so far

		void Initialise(int numberOfNeurons, int jitter);
		void InitScanner();

		inline void MatchAllTemplates(String^ tag);
		inline void MatchOneTemplate(MatchTemplate^ pattern, int offset, String^ tag);

		void HandleAggregatedMatch(String^ tag, int templateID, int uniqueID, int timeIndex, int matchCount, String^ matchPattern, array<FiringEvent>^ coincidentFiringEvents);

		long GetLastAccess(int i);
		int GetFileIndex(int currentFileIndex, int requiredOffset);
		List<FiringEvent>^ GetEventData(String^ filePath);
		List<FiringEvent>^ GetFirings(int fileIndex);

		void CreateSpikeRaster(List<FiringEvent>^ firings);
		void FillSpikeRaster(List<FiringEvent>^ firings, array<int, 2>^ spikeRaster, int offset);
		void ClearSpikeRaster(int offset);

	public:
		event MatchedEventHandler^ Matched;
		event ScanUpdateEventHandler^ ScanUpdate;

		property List<MatchTemplate^>^ Templates;				// a store for added templates

		MatchScanner(int numberOfNeurons, int jitter);
		MatchScanner(Dictionary<int, String^>^ filesDictionary, int numberOfNeurons, int jitter);

		void Reset();
		void SaveData(String^ filePath);
		void SaveData(String^ filePath, int fileIndex);
		void AddData(int fileIndex, List<FiringEvent>^ data);		// add firings data (for in-memory scans)
		void AddFile(int fileIndex, String^ filePath);				// add a file (for file-based scans)
		String^ GetFilePath(int fileIndex) { return _filesDictionary[fileIndex]; }
		int GetNextFileIndex(int currentFileIndex) { return this->GetFileIndex(currentFileIndex, 1); }
		int GetPreviousFileIndex(int currentFileIndex) { return this->GetFileIndex(currentFileIndex, -1); }

		void AddTemplate(Pattern^ pattern, int matchThreshold);
		int AddTemplate(String^ firingsString, int matchThreshold);				// for compatibility w/ Python etc.
		void ClearTemplates();

		void Match(String^ tag);

		MatchRaster GetMatch(MatchResult result);
		MatchRaster GetMatch(int templateID, int fileIndex, int offset);
		int ScoreMatch(MatchRaster match);
	};
}